Хорошо, в настоящее время я извлекаю тысячи DOI из текста в свободной форме (XML), и я понял, что мой предыдущий подход имел несколько проблем, а именно, касающихся закодированных сущностей и конечной пунктуации, поэтому я продолжил Чтение спецификации , и это лучшее, что я могу получить.
Префикс DOI должен состоять из указателя каталога, за которым следует
код регистранта. Эти два компонента должны быть разделены
остановка (период).
Индикатор каталога должен быть «10». Индикатор каталога
различает весь набор символьных строк (префикс и суффикс)
в качестве цифровых идентификаторов объектов в системе разрешения.
Достаточно просто, начальный \b
не позволяет нам "сопоставить" "DOI", который не начинается с 10.
:
$pattern = '\b(10[.]';
Вторым элементом префикса DOI должен быть код регистранта.
Код регистранта - это уникальная строка, присваиваемая регистранту.
Кроме того, все присвоенные коды регистрантов являются числовыми и имеют длину не менее 4 цифр, поэтому:
$pattern = '\b(10[.][0-9]{4,}';
Код регистранта может быть дополнительно разделен на подэлементы для
административное удобство при желании. Каждый подэлемент
Коду регистранта должен предшествовать полный останов.
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*';
Синтаксис DOI должен состоять из префикса DOI и суффикса DOI
разделены косой чертой.
Однако это не является абсолютно необходимым, в разделе 2.2.3 говорится, что необычные суффиксные системы могут использовать другие соглашения (например, 10.1000.123456
вместо 10.1000/123456
), но позволяют немного ослабить.
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/';
В имени DOI регистр не учитывается и может включать любой печатный
символы из легальных графических символов Unicode. DOI
суффикс должен состоять из строки символов любой длины, выбранной
регистранте. Каждый суффикс должен быть уникальным для элемента префикса, который
предшествует этому. Уникальный суффикс может быть последовательным числом или может
включить идентификатор, сгенерированный из другой системы или основанный на ней.
Теперь все становится сложнее, из всех обработанных мной DOI я увидел следующие символы (кроме, конечно, [0-9a-zA-Z]
) в суффиксах : .-()/:-
- так что пока его не существует, DOI 10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
вполне правдоподобно.
Логическим выбором будет использование \S
или [[:graph:]]
PCRE POSIX класса, поэтому давайте сделаем это:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/\S+'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/[[:graph:]]+';
Теперь у нас сложная проблема, класс [[:graph:]]
- это супер-набор класса [[:punct:]]
, который включает символы, которые можно легко найти в произвольном тексте или на любом языке разметки: "'&<>
среди других.
Давайте пока отфильтруем разметки, используя негативный взгляд:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+';
Вышеуказанное должно охватывать закодированные объекты (&
), кавычки атрибутов (["']
) и теги открытия / закрытия ([<>]
).
В отличие от языков разметки, в свободном тексте обычно не используются знаки пунктуации, если они не ограничены хотя бы одним пробелом или , помещенным в конце предложения, например:
Это длинный DOI:
10.1016.12.31/nature.S0735-1097(98)2000/12/31/34:7-7
!!!
Решение здесь состоит в том, чтобы закрыть нашу группу захвата и установить границу другого слова:
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b'; // or
$pattern = '\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])[[:graph:]])+)\b';
И вуаля , вот демка .